﻿
// **************************************************************************
//    Class Created by Mick Doherty (Dotnetrix) March 2010
//    http://dotnetrix.co.uk/
//
//    ...for GlassUI (an AeroGlass UI Library)
//    http://glassui.codeplex.com/
// **************************************************************************

using System;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

namespace GlassUI
{
    //A utilities class for the common Win32 Macros and other useful methods and properties
    public static class GlassUtilities
    {
        #region WIN32 Macros 

        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "HIWORD")]
        public static int HIWORD(int value)
        {
            if ((value & 0x80000000) == 0x80000000)
                return (value >> 16);
            else
                return (value >> 16) & 0xffff;
        }

        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "HIWORD")]
        public static int HIWORD(IntPtr value)
        {
            return HIWORD(value.ToInt32());
        }

        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "LOWORD")]
        public static int LOWORD(int value)
        {
            return value & 0xffff;
        }

        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "LOWORD")]
        public static int LOWORD(IntPtr value)
        {
            return LOWORD(value.ToInt32());
        }

        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "MAKELONG")]
        public static int MAKELONG(int lo, int hi)
        {
            return (hi << 16) | (lo & 0xffff);
        }

        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "MAKELPARAM")]
        public static IntPtr MAKELPARAM(int lo, int hi)
        {
            return (IntPtr)((hi << 16) | (lo & 0xffff));
        }

        #endregion

        #region public static properties

        /// <summary>
        /// Returns true if the current OS supports Aero Glass.
        /// </summary>
        public static bool GlassSupported
        {
            get
            {
                return Environment.OSVersion.Version.Major >= 6;
            }
        }

        /// <summary>
        /// Returns true if the current System has Aero Glass enabled.
        /// </summary>
        public static bool GlassEnabled
        {
            get
            {
                bool glassSupport = false;
                if (GlassSupported)
                {
                    NativeMethods.DWMAPI.DwmIsCompositionEnabled(out glassSupport);
                }
                return glassSupport;
            }
        }

        /// <summary>
        /// Returns the Color which the OS uses to paint Aero Glass.
        /// </summary>
        public static Color SystemGlassColor
        {
            get
            {
                if (GlassEnabled)
                {
                    int color;
                    bool opaque;
                    if (NativeMethods.DWMAPI.DwmGetColorizationColor(out color, out opaque).ToInt32() == NativeMethods.USER32.S_OK)
                        return Color.FromArgb(color);
                }
                return Color.Empty;
            }
        }

        #endregion

        #region public static methods

        /// <summary>
        /// Creates a Rectangle with rounded corners.
        /// </summary>
        /// <param name="bounds">
        /// The bounds of the Rectangle to create.
        /// </param>
        /// <param name="radius">
        /// The radius to be used for the corners. This will be reduced to half of width/height if it exceeds either.
        /// </param>
        /// <returns>
        /// A GraphicsPath representing the Rectangle with all corners rounded.
        /// </returns>
        public static GraphicsPath RoundedRectangle(Rectangle bounds, int radius)
        {
            return RoundedRectangle(bounds, radius, Corners.All);
        }

        /// <summary>
        /// Creates a Rectangle with specified corners rounded.
        /// </summary>
        /// <param name="bounds">
        /// The bounds of the Rectangle to create.
        /// </param>
        /// <param name="radius">
        /// The radius to be used for the corners. This will be reduced to half of width/height if it exceeds either.
        /// </param>
        /// <param name="corners">
        /// The corners which are desired to be rounded.
        /// </param>
        /// <returns>
        /// A GraphicsPath representing the Rectangle with specified corners rounded.
        /// </returns>
        public static GraphicsPath RoundedRectangle(Rectangle bounds, int radius, Corners corners)
        {
            //Make sure the Path fits inside the rectangle
            bounds.Width --;
            bounds.Height --;

            //Scale the radius if it's too large to fit.
            if (radius > (bounds.Width))
                radius = bounds.Width;
            if (radius > (bounds.Height))
                radius = bounds.Height;

            System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();

            if (radius <= 0)
                path.AddRectangle(bounds);
            else
            {
                if ((corners & Corners.TopLeft) == Corners.TopLeft)
                    path.AddArc(bounds.Left, bounds.Top, radius, radius, 180, 90);
                else
                    path.AddLine(bounds.Left, bounds.Top, bounds.Left, bounds.Top);

                if ((corners & Corners.TopRight) == Corners.TopRight)
                    path.AddArc(bounds.Right - radius, bounds.Top, radius, radius, 270, 90);
                else
                    path.AddLine(bounds.Right, bounds.Top, bounds.Right, bounds.Top);

                if ((corners & Corners.BottomRight) == Corners.BottomRight)
                    path.AddArc(bounds.Right - radius, bounds.Bottom - radius, radius, radius, 0, 90);
                else
                    path.AddLine(bounds.Right, bounds.Bottom, bounds.Right, bounds.Bottom);

                if ((corners & Corners.BottomLeft) == Corners.BottomLeft)
                    path.AddArc(bounds.Left, bounds.Bottom - radius, radius, radius, 90, 90);
                else
                    path.AddLine(bounds.Left, bounds.Bottom, bounds.Left, bounds.Bottom);
            }

            path.CloseFigure();

            return path;

        }

        /// <summary>
        /// A method to darken a color by a percentage of the difference between the color and Black.
        /// </summary>
        /// <param name="colorIn">
        /// The original color.
        /// </param>
        /// <param name="percent">
        /// The percentage by which to darken the original color.
        /// </param>
        /// <returns>
        /// The return color's Alpha value will be unchanged, but the RGB content will
        /// have been increased by the specified percentage. If percent is 100 then the
        /// returned Color will be Black with original Alpha.
        /// </returns>
        public static Color DarkenColor(Color colorIn, int percent)
        {
            if (percent < 0 || percent > 100)
                throw new ArgumentOutOfRangeException("percent");

            int a, r, g, b;

            a = colorIn.A;
            r = colorIn.R - (int)((colorIn.R / 100f) * percent);
            g = colorIn.G - (int)((colorIn.G / 100f) * percent);
            b = colorIn.B - (int)((colorIn.B / 100f) * percent);

            return Color.FromArgb(a, r, g, b);
        }

        /// <summary>
        /// A method to lighten a color by a percentage of the difference between the color and Black.
        /// </summary>
        /// <param name="colorIn">
        /// The original color.
        /// </param>
        /// <param name="percent">
        /// The percentage by which to lighten the original color.
        /// </param>
        /// <returns>
        /// The return color's Alpha value will be unchanged, but the RGB content will
        /// have been decreased by the specified percentage. If percent is 100 then the
        /// returned Color will be White with original Alpha.
        /// </returns>
        public static Color LightenColor(Color colorIn, int percent)
        {
            if (percent < 0 || percent > 100)
                throw new ArgumentOutOfRangeException("percent");

            int a, r, g, b;

            a = colorIn.A;
            r = colorIn.R + (int)(((255f - colorIn.R) / 100f) * percent);
            g = colorIn.G + (int)(((255f - colorIn.G) / 100f) * percent);
            b = colorIn.B + (int)(((255f - colorIn.B) / 100f) * percent);

            return Color.FromArgb(a, r, g, b);
        }

        /// <summary>
        /// A method to draw Glowing text if the operating system supports it.
        /// If the os doe snot support it then standard GDi text is drawn.
        /// </summary>
        /// <param name="graphics">The Graphics object on which to draw.</param>
        /// <param name="text">The text to draw.</param>
        /// <param name="font">The font used to draw text.</param>
        /// <param name="bounds">The bounding rectangle within which to draw.</param>
        /// <param name="textColor">The color used to draw the text.</param>
        /// <param name="format">The text format flags to apply.</param>
        /// <param name="glowSize">The size in pixels of the glow around text.</param>
        /// <returns></returns>
        public static IntPtr DrawThemedText(Graphics graphics, string text, Font font, Rectangle bounds, Color textColor, TextFormatFlags format, int glowSize)
        {
            return DrawThemedText(graphics, text, font, bounds, textColor, format, glowSize, ToolStripTextDirection.Horizontal);
        }

        /// <summary>
        /// A method to draw Glowing text if the operating system supports it.
        /// If the os doe snot support it then standard GDi text is drawn.
        /// </summary>
        /// <param name="graphics">The Graphics object on which to draw.</param>
        /// <param name="text">The text to draw.</param>
        /// <param name="font">The font used to draw text.</param>
        /// <param name="bounds">The bounding rectangle within which to draw.</param>
        /// <param name="textColor">The color used to draw the text.</param>
        /// <param name="format">The text format flags to apply.</param>
        /// <param name="glowSize">The size in pixels of the glow around text.</param>
        /// <param name="textDirection">The direction to draw text.</param>
        /// <returns></returns>
        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        public static IntPtr DrawThemedText(Graphics graphics, string text, Font font, Rectangle bounds, Color textColor, TextFormatFlags format, int glowSize, ToolStripTextDirection textDirection)
        {
            IntPtr result = (IntPtr)NativeMethods.USER32.S_OK;

            if (!GlassEnabled)
            {
                TextRenderer.DrawText(graphics, text, font, bounds, textColor, format);
                return result;
            }

            Region clip = graphics.Clip;
            IntPtr hRgn = clip.GetHrgn(graphics);

            IntPtr sourceDC = graphics.GetHdc();

            IntPtr memDC = NativeMethods.GDI32.CreateCompatibleDC(sourceDC);

            NativeMethods.GDI32.BITMAPINFOHEADER bmiHdr = new NativeMethods.GDI32.BITMAPINFOHEADER();
            bmiHdr.biSize = Marshal.SizeOf(bmiHdr);
            bmiHdr.biBitCount = 32;
            bmiHdr.biWidth = bounds.Width;
            bmiHdr.biHeight = -bounds.Height;
            bmiHdr.biPlanes = 1;
            bmiHdr.biBitCount = 32;
            bmiHdr.biCompression = NativeMethods.GDI32.BITMAPCOMPRESSION.RGB;

            NativeMethods.GDI32.BITMAPINFO bi = new NativeMethods.GDI32.BITMAPINFO();
            bi.biHdr = bmiHdr;

            IntPtr dib = NativeMethods.GDI32.CreateDIBSection(sourceDC, ref bi, 0, IntPtr.Zero, IntPtr.Zero, 0);
            NativeMethods.GDI32.SelectObject(memDC, dib);

            NativeMethods.GDI32.BitBlt(memDC, 0, 0, bounds.Width, bounds.Height, sourceDC, bounds.Left, bounds.Top, NativeMethods.USER32.SRCPAINT);

            IntPtr hFont = font.ToHfont();
            IntPtr originalFont = NativeMethods.GDI32.SelectObject(memDC, hFont);

            VisualStyleRenderer renderer = new VisualStyleRenderer(VisualStyleElement.CreateElement("globals", 0, 0));

            NativeMethods.UXTHEME.DTTOPTS options = new NativeMethods.UXTHEME.DTTOPTS();
            options.dwSize = Marshal.SizeOf(options);

            options.dwFlags = NativeMethods.UXTHEME.DrawThemeTextFlags.DTT_COMPOSITED | NativeMethods.UXTHEME.DrawThemeTextFlags.DTT_GLOWSIZE | NativeMethods.UXTHEME.DrawThemeTextFlags.DTT_TEXTCOLOR;

            options.crText = ColorTranslator.ToWin32(textColor);
            options.iGlowSize = glowSize;

            int glowOffset = glowSize / 2;

            NativeMethods.USER32.RECT textBounds = new NativeMethods.USER32.RECT(new Rectangle(glowOffset, glowOffset, bounds.Width - glowSize, bounds.Height - glowSize));

            result = NativeMethods.UXTHEME.DrawThemeTextEx(renderer.Handle, memDC, 0, 0, text, -1, format, ref textBounds, ref options);

            if (result.ToInt32() == NativeMethods.USER32.S_OK)
            {
                NativeMethods.GDI32.SelectObject(sourceDC, hRgn);
                NativeMethods.GDI32.BitBlt(sourceDC, bounds.Left, bounds.Top, bounds.Width, bounds.Height, memDC, 0, 0, NativeMethods.USER32.SRCCOPY);
                NativeMethods.GDI32.DeleteObject(hRgn);
            }
            NativeMethods.GDI32.SelectObject(sourceDC, originalFont);

            NativeMethods.GDI32.DeleteObject(hFont);
            NativeMethods.GDI32.DeleteObject(dib);
            NativeMethods.GDI32.DeleteDC(memDC);

            graphics.ReleaseHdc(sourceDC);

            return result;
        }

        /// <summary>
        /// A method to measure the size necessary in which to fit the text.
        /// </summary>
        /// <param name="graphics">The Graphics object on which to draw.</param>
        /// <param name="text">The text to draw.</param>
        /// <param name="font">The font used to draw text.</param>
        /// <param name="bounds">The bounding rectangle within which to draw.</param>
        /// <param name="textColor">The color used to draw the text.</param>
        /// <param name="format">The text format flags to apply.</param>
        /// <param name="glowSize">The size in pixels of the glow around text.</param>
        /// <returns>The size needed to draw text with glow.</returns>
        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        public static Size MeasureThemedText(Graphics graphics, string text, Font font, Size size, TextFormatFlags format, int glowSize)
        {
            Size sz = Size.Empty;

            if (GlassEnabled)
            {
                VisualStyleRenderer renderer = new VisualStyleRenderer(VisualStyleElement.TextBox.TextEdit.Normal);

                IntPtr hTheme = renderer.Handle;

                IntPtr sourceDC = graphics.GetHdc();

                IntPtr hFont = font.ToHfont();
                IntPtr originalFont = NativeMethods.GDI32.SelectObject(sourceDC, hFont);

                NativeMethods.USER32.RECT rc;

                NativeMethods.USER32.RECT rcIn = new NativeMethods.USER32.RECT(new Rectangle(Point.Empty, size));

                if (NativeMethods.UXTHEME.GetThemeTextExtent(hTheme, sourceDC, 0, 0, text, -1, (int)format, ref rcIn, out rc) == NativeMethods.USER32.S_OK)
                    sz = rc.ToRectangle().Size;

                graphics.ReleaseHdc();

                NativeMethods.GDI32.SelectObject(sourceDC, originalFont);
                NativeMethods.GDI32.DeleteObject(hFont);

                sz.Width += glowSize;
                sz.Height += glowSize;

            }
            else
            {
                sz = TextRenderer.MeasureText(text, font, size, format);
            }

            return sz;
        }

        /// <summary>
        /// A method used to draw standard Image and Text content with standard layout options.
        /// </summary>
        /// <param name="graphics">The Graphics object on which to draw.</param>
        /// <param name="bounds">The bounding Rectangle within which to draw.</param>
        /// <param name="text">The text to draw.</param>
        /// <param name="font">The font used to draw text.</param>
        /// <param name="image">The image to draw (this may be null).</param>
        /// <param name="textAlignment">The vertical and horizontal alignment of the text.</param>
        /// <param name="imageAlignment">The vertical and horizontal alignment of the image.</param>
        /// <param name="textImageRelation">The placement of the image and text relative to each other.</param>
        /// <param name="textColor">The color used to draw text.</param>
        /// <param name="wordWrap">set true if text should wrap.</param>
        /// <param name="glowSize">The size in pixels of the glow around text.</param>
        public static void RenderStandardContent(Graphics graphics, Rectangle bounds, String text, Font font, Image image, System.Drawing.ContentAlignment textAlignment, System.Drawing.ContentAlignment imageAlignment, TextImageRelation textImageRelation, Color textColor, bool wordWrap, int glowSize)
        {
            RenderStandardContent(graphics, bounds, text, font, image, textAlignment, imageAlignment, textImageRelation, textColor, wordWrap, glowSize, true, TextFormatFlags.TextBoxControl);
        }

        public static void RenderStandardContent(Graphics graphics, Rectangle bounds, String text, Font font, Image image, System.Drawing.ContentAlignment textAlignment, System.Drawing.ContentAlignment imageAlignment, TextImageRelation textImageRelation, Color textColor, bool wordWrap, int glowSize, bool enabled)
        {
            RenderStandardContent(graphics, bounds, text, font, image, textAlignment, imageAlignment, textImageRelation, textColor, wordWrap, glowSize, enabled, TextFormatFlags.TextBoxControl);
        }

        /// <summary>
        /// A method used to draw standard Image and Text content with standard layout options.
        /// </summary>
        /// <param name="graphics">The Graphics object on which to draw.</param>
        /// <param name="bounds">The bounding Rectangle within which to draw.</param>
        /// <param name="text">The text to draw.</param>
        /// <param name="font">The font used to draw text.</param>
        /// <param name="image">The image to draw (this may be null).</param>
        /// <param name="textAlignment">The vertical and horizontal alignment of the text.</param>
        /// <param name="imageAlignment">The vertical and horizontal alignment of the image.</param>
        /// <param name="textImageRelation">The placement of the image and text relative to each other.</param>
        /// <param name="textColor">The color used to draw text.</param>
        /// <param name="wordWrap">set true if text should wrap.</param>
        /// <param name="glowSize">The size in pixels of the glow around text.</param>
        /// <param name="enabled">Draws the content grayed if enabled = false.</param>
        public static void RenderStandardContent(Graphics graphics, Rectangle bounds, String text, Font font, Image image, System.Drawing.ContentAlignment textAlignment, System.Drawing.ContentAlignment imageAlignment, TextImageRelation textImageRelation, Color textColor, bool wordWrap, int glowSize, bool enabled, TextFormatFlags format)
        {
            bool horizontalRelation = (int)textImageRelation > 2;
            bool imageHasPreference = textImageRelation == TextImageRelation.ImageBeforeText || textImageRelation == TextImageRelation.ImageAboveText;

            int preferredAlignmentValue = imageHasPreference ? (int)imageAlignment : (int)textAlignment;

            Rectangle contentRectangle = bounds;

            format |= TextFormatFlags.TextBoxControl;

            if (wordWrap)
                format |= TextFormatFlags.WordBreak;
            else
                format |= TextFormatFlags.SingleLine;


            // Get ImageSize
            Size imageSize = image == null ? Size.Empty : image.Size;

            // Get AvailableTextSize
            Size availableTextSize = horizontalRelation ? 
                new Size(bounds.Width-imageSize.Width, bounds.Height) :
                new Size(bounds.Width, bounds.Height-imageSize.Height);

            // Get ActualTextSize
            Size actualTextSize = GlassUtilities.MeasureThemedText(graphics, text, font, availableTextSize, format, 8);

            // Get ContentRectangle based upon TextImageRelation
            if (textImageRelation != 0)
            {
                // Get ContentSize
                Size contentSize = horizontalRelation ?
                    new Size(imageSize.Width + actualTextSize.Width, Math.Max(imageSize.Height, availableTextSize.Height)) :
                    new Size(Math.Max(imageSize.Width, availableTextSize.Width), imageSize.Height + actualTextSize.Height);

                // Get ContentLocation
                Point contentLocation = bounds.Location;
                if (horizontalRelation)
                {
                    if (preferredAlignmentValue % 15 == 1)
                        contentLocation.X = bounds.Left;
                    else if (preferredAlignmentValue % 15 == 2)
                        contentLocation.X = bounds.Left + (bounds.Width / 2 - contentSize.Width / 2);
                    else if (preferredAlignmentValue % 15 == 4)
                        contentLocation.X = bounds.Right - contentSize.Width;
                }
                else
                {
                    if (preferredAlignmentValue <= 4)
                        contentLocation.Y = bounds.Top;
                    else if (preferredAlignmentValue >= 256)
                        contentLocation.Y = bounds.Bottom - contentSize.Height;
                    else
                        contentLocation.Y = bounds.Top + (bounds.Height / 2 - contentSize.Height / 2);
                }

                contentRectangle = new Rectangle(contentLocation, contentSize);
            }

            if (image != null)
            {
                // Get ActualImageBounds
                Rectangle actualImageBounds = new Rectangle(bounds.Location, imageSize);
                if (horizontalRelation)
                {
                    if (imageHasPreference)
                    {
                        actualImageBounds.X = contentRectangle.X;
                    }
                    else
                    {
                        actualImageBounds.X = contentRectangle.Right - imageSize.Width;
                    }
                    actualImageBounds.Y = (int)imageAlignment <= 4 ?
                        contentRectangle.Y : (int)imageAlignment >= 256 ?
                        contentRectangle.Bottom - imageSize.Height :
                        contentRectangle.Y + contentRectangle.Height / 2 - imageSize.Height / 2;
                }
                else if (textImageRelation == 0)
                {
                    if ((int)imageAlignment <= 4 )
                        actualImageBounds.Y = bounds.Top;
                    else if ((int)imageAlignment >= 256)
                        actualImageBounds.Y = bounds.Bottom - imageSize.Height;
                    else
                        actualImageBounds.Y = bounds.Top + (bounds.Height / 2 - imageSize.Height / 2);

                    if ((int)imageAlignment % 15 == 1)
                        actualImageBounds.X = bounds.Left;
                    else if ((int)imageAlignment % 15 == 2)
                        actualImageBounds.X = bounds.Left + (bounds.Width / 2 - imageSize.Width / 2);
                    else if ((int)imageAlignment % 15 == 4)
                        actualImageBounds.X = bounds.Right - imageSize.Width;
                }
                else
                {
                    if (imageHasPreference)
                    {
                        actualImageBounds.Y = contentRectangle.Y;
                    }
                    else
                    {
                        actualImageBounds.Y = contentRectangle.Bottom - imageSize.Height;
                    }
                    actualImageBounds.X = (int)imageAlignment % 15 == 1 ?
                        contentRectangle.X : (int)imageAlignment % 15 == 2 ?
                        contentRectangle.X + contentRectangle.Width / 2 - imageSize.Width / 2 :
                        contentRectangle.Right - imageSize.Width;
                }

                // Draw Image
                if (enabled)
                    graphics.DrawImage(image, actualImageBounds);
                else
                    ControlPaint.DrawImageDisabled(graphics, image, actualImageBounds.X, actualImageBounds.Y, Color.Transparent);
            }

            // Get ActualTextBounds
            Rectangle actualTextBounds = new Rectangle(Point.Empty, actualTextSize);
            if (horizontalRelation)
            {
                if (imageHasPreference)
                {
                    actualTextBounds.X = contentRectangle.Right - actualTextSize.Width;
                }
                else
                {
                    actualTextBounds.X = contentRectangle.X;
                }
                actualTextBounds.Y = (int)textAlignment <= 4 ?
                    contentRectangle.Y : (int)textAlignment >= 256 ?
                    contentRectangle.Bottom - actualTextSize.Height :
                    contentRectangle.Y + (contentRectangle.Height / 2) - (actualTextSize.Height / 2);
            }
            else if (textImageRelation == 0)
            {
                if ((int)textAlignment <= 4)
                    actualTextBounds.Y = bounds.Top;
                else if ((int)textAlignment >= 256)
                    actualTextBounds.Y = bounds.Bottom - actualTextSize.Height;
                else
                    actualTextBounds.Y = bounds.Top + (bounds.Height / 2 - actualTextSize.Height / 2);

                if ((int)textAlignment % 15 == 1)
                    actualTextBounds.X = bounds.Left;
                else if ((int)textAlignment % 15 == 2)
                    actualTextBounds.X = bounds.Left + (bounds.Width / 2 - actualTextSize.Width / 2);
                else if ((int)textAlignment % 15 == 4)
                    actualTextBounds.X = bounds.Right - actualTextSize.Width;
            }
            else
            {
                if (imageHasPreference)
                {
                    actualTextBounds.Y = contentRectangle.Bottom - actualTextSize.Height;
                }
                else
                {
                    actualTextBounds.Y = contentRectangle.Y;
                }
                actualTextBounds.X = (int)textAlignment % 15 == 1 ?
                    contentRectangle.X : (int)textAlignment % 15 == 2 ?
                    contentRectangle.X + contentRectangle.Width / 2 - actualTextSize.Width / 2 :
                    contentRectangle.Right - actualTextSize.Width;
            }

            // Draw text
            GlassUtilities.DrawThemedText(graphics, text, font, actualTextBounds, textColor, format, glowSize);

        }

        /// <summary>
        /// Draws a Stone.
        /// </summary>
        /// <param name="graphics">The Graphics object on which to draw.</param>
        /// <param name="bounds">The bounding rectangle to draw within.</param>
        /// <param name="color">The color of the stone.</param>
        public static void DrawStone(Graphics graphics, RectangleF bounds, Color color)
        {
            SizeF scale = new SizeF(bounds.Width / 100, bounds.Height / 100);

            using (GraphicsPath stonePath = new GraphicsPath())
            {
                stonePath.AddEllipse(bounds);

                using (PathGradientBrush stoneBrush = new PathGradientBrush(stonePath))
                {
                    stoneBrush.CenterPoint = new PointF(bounds.Left - (20 * scale.Width), bounds.Top - (20 * scale.Height));
                    stoneBrush.CenterColor = Color.White;
                    stoneBrush.SetSigmaBellShape(0.96f);

                    Color stoneColor = color;

                    if (stoneColor.GetBrightness() >= 0.8)
                    {
                        stoneColor = DarkenColor(stoneColor, 16);
                    }

                    stoneBrush.SurroundColors = new Color[] { stoneColor };

                    graphics.FillEllipse(stoneBrush, bounds);

                    stoneBrush.CenterPoint = new PointF(bounds.Right + (20 * scale.Width), bounds.Bottom + (20 * scale.Height));
                    stoneBrush.SurroundColors = new Color[] { Color.Empty };
                    graphics.FillEllipse(stoneBrush, bounds);
                }

            }

            using (Pen linePen = new Pen(color))
            {
                graphics.DrawEllipse(linePen, bounds);

            }
        }

        public static void DrawGlowGradient(Graphics graphics, Rectangle bounds, Color backColor, Color glowColor, ControlState state)
        {
            Color topColor = Color.FromArgb(128, Color.White);
            Color bottomColor = Color.FromArgb(128, Color.Black);

            using (Brush brush = new SolidBrush(backColor))
                graphics.FillRectangle(brush, bounds);

            if (state == ControlState.Pressed)
            {
                // Set up the Gradient Color
                topColor = glowColor;
                // Set up the Gradient Fade Color
                bottomColor = backColor;
            }

            // Fill with the Gradient
            using (LinearGradientBrush brush = new LinearGradientBrush(bounds, Color.FromArgb(192, topColor), bottomColor, LinearGradientMode.Vertical))
            {
                brush.WrapMode = WrapMode.TileFlipX;
                graphics.FillRectangle(brush, bounds);
            }

            // Don't blend glow
            graphics.CompositingMode = CompositingMode.SourceOver;

            // Draw a Glow
            if (state == ControlState.Hot)
            {
                bounds.Offset(0, bounds.Height / 2);

                using (GraphicsPath glowPath = new GraphicsPath())
                {
                    glowPath.AddEllipse(bounds);

                    using (PathGradientBrush brush = new PathGradientBrush(glowPath))
                    {
                        brush.CenterColor = glowColor;
                        brush.SurroundColors = new Color[] { Color.Transparent };
                        graphics.SetClip(glowPath, CombineMode.Replace);
                        graphics.FillPath(brush, glowPath);
                        graphics.ResetClip();
                    }
                }
            }
        }

        public static void DrawRoundedBorder(Graphics graphics, Rectangle bounds, int radius, Corners corners, Color glowColor)
        {
            bounds.Width--;
            bounds.Height--;
            using (Pen pen = new Pen(Color.FromArgb(192, Color.Black)))
            {
                using (GraphicsPath path = GlassUtilities.RoundedRectangle(bounds, radius, corners))
                {
                    graphics.DrawPath(pen, path);
                    bounds.Inflate(-1, -1);
                    path.Reset();
                    path.AddPath(GlassUtilities.RoundedRectangle(bounds, radius - 1, corners), false);
                    pen.Color = glowColor.IsEmpty ? Color.FromArgb(192, Color.White) : glowColor;
                    graphics.DrawPath(pen, path);
                }
            }
        }

        #endregion
        
    }

}
